/* Nate Deisinger (ndeisinger@wisc.edu)
 *
 * This file is part of soot-csi, a project to apply lightweight
 * instrumentation to Java programs to facilitate debugging through
 * the Soot analysis framework.
 *
 * Copyright (c) 2014, Nate Deisinger, University of Wisconsin-Madison
 *   Madison, Wisconsin 53715
 *   All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor,
 *   Boston, MA  02110-1301
 *   USA
 * or retrieve version 2.1 at their website:
 *   http://www.gnu.org/licenses/lgpl-2.1.html
 */
package uwmadison.soot.analysis;

/**
 * Auxiliary class to support path profiling data collection.
 * When the program throws an exception, a 'shadow stack' of these objects
 * will be generated as the stack unwinds.  
 * 
 * If the exception is caught and handled, the shadow stack will be freed on the
 * next successful function return.
 * 
 * This class is almost entirely the same as CallTrace; it would probably be 
 * best to, in the future, create a super "Trace" class and subclass that to
 * create these two classes.
 * 
 * @author Nate Deisinger
 */
public class PathTrace {
    private final long[] recentPaths = new long[10];
    public long recentPath; //Making public saves getter/setter overhead
    protected PathTrace enclosing;
    protected PathTrace encloses;
    protected String methodName;
    private static PathTrace innermost;
    private static PathTrace firstFree;
    private static PathTrace initial;
    private static final PathTrace dummyTrace;
    public static boolean inUse; //Only do full work when crashing
    
    /**
     * Initial class setup to pre-generate entries.
     */
    static
    {
        //Perform setup - that is, generate 50 entries
        firstFree = new PathTrace();
        initial = firstFree;
        innermost = null;
        PathTrace currEnt = firstFree;
        for (int i = 0; i < 50; i++)
        {
            PathTrace newEnt = new PathTrace();     
            currEnt.encloses = newEnt;
            newEnt.enclosing = currEnt;
            currEnt = newEnt;
        }
        dummyTrace = new PathTrace();
        inUse = false;
    }

    /**
     * Creates an empty PathTrace object.
     */
    public PathTrace() {
        methodName = "";
        recentPath = 0;
        enclosing = null;
        encloses = null;
        return;
    }

    /**
     * Creates a PathTrace object for the given method.
     */
    public PathTrace(String methodName) {
        this.methodName = methodName;
        recentPath = 0;
        enclosing = null;
        encloses = null;
        return;
    }
    
    /**
     * Returns a dummy trace object.  Used for performance debugging.
     */
    public static PathTrace getRef_dummy(String name)
    {
        return dummyTrace;
    }
    
    /**
     * Return a reference to a new PathTrace object.  Generates
     * additional objects if all are used.
     */
    public static PathTrace getRef(String name)
    {        
         if (firstFree == null)
         {
            //Need to generate more entries; for now do 20
            PathTrace currEnt = innermost;
            for (int i = 0; i < 20; i++)
            {
                PathTrace newEnt = new PathTrace();     
                currEnt.encloses = newEnt;
                newEnt.enclosing = currEnt;
                currEnt = newEnt;
            }
            firstFree = innermost.encloses;
         }
        PathTrace myTrace = firstFree;
        firstFree = myTrace.encloses;
        myTrace.methodName = name;
        inUse = true;
        return myTrace;
    }

    /**
     * Dumps our method name and path value.
     */
    protected void dumpTrace()
    {
        System.err.println("Method name: " + methodName + " Most recent path: " + recentPath);
        return;
    }
    
    /**
     * Dummy close method used in performance analysis.
     */
    public void close_dummy() {
        //innermost = this.enclosing;
        //firstFree = this;
        int l = 4 + 4;
        return;
    }

    /**
     * Re-initializes the stack for us.
     */
    public static void freeStack()
    {
        firstFree = initial;
        inUse = false;
    }

    /**
     * Statically dumps our traces.
     */
    static public void dumpAllTraces() {
        System.err.println("Local path coverage:");
        for (PathTrace current = firstFree.enclosing;
                current != null; current = current.enclosing)
        {
            current.dumpTrace();
        }
        return;
    }
} 